#include "stdafx.h"
#include <iostream>
#include <string>
#include <chrono>
#include "test_image_loader.h"
#include "Table.h"
#include "Column.h"
#undef max

test_image_loader::test_image_loader()
{
}


test_image_loader::~test_image_loader()
{
}

void test_image_loader::initialize()
{
	m_jet.reset(new ese::jet());
}

void test_image_loader::prepare()
{
	std::cout << "====================================" << std::endl
		<< "\t JET Instance" << std::endl
		<< "====================================" << std::endl;
	m_jet->init_start_instance();
	std::cout << " ID: " << m_jet->id() << std::endl;
	std::cout << " Is circular log: " << m_jet->circular_log() << std::endl;
	std::cout << " Last Error: " << m_jet->last_error() << std::endl;
	std::cout << " Log Path: " << m_jet->log_path() << std::endl;
	std::cout << " Maximum Log Size: " << m_jet->log_size() << std::endl;
	std::cout << " Maximum Sessions: " << m_jet->max_sessions() << std::endl;
	std::cout << " Maximum transaction pages: " << m_jet->max_transaction_pages() << std::endl;
	std::cout << " System Path: " << m_jet->system_path() << std::endl;
	std::cout << "====================================" << std::endl << std::endl;
	if (m_jet->last_error() != JET_errSuccess)
	{
		throw std::exception(ese::error(m_jet->last_error()).c_str());
	}// if

	std::cout << "====================================" << std::endl
		<< "\t Session" << std::endl
		<< "====================================" << std::endl;
	m_ses.reset(new ese::session(*m_jet));
	bool is_session_ok = m_ses->start();
	std::cout << " ID: " << m_ses->id() << std::endl;
	std::cout << " Last Error: " << m_ses->last_error() << std::endl;
	std::cout << "====================================" << std::endl << std::endl;
	if (!is_session_ok)
	{
		throw std::exception(ese::error(m_ses->last_error()).c_str());
	}// if


	std::cout << "====================================" << std::endl
		<< "\t Database" << std::endl
		<< "====================================" << std::endl;
	m_db.reset(new ese::database(*m_ses));
	path pDb((ese::exe_path().parent_path() /= "Data") /= "test_db.edb");
	m_db_file_name = pDb;
	if (!m_db->open(m_db_file_name))
	{
		if (m_db->last_error() == JET_errFileNotFound)
		{
			std::cout << "\tCreating the Database: " << m_db_file_name << std::endl;
			if (!m_db->create(m_db_file_name))
			{
				throw std::exception(ese::error(m_db->last_error()).c_str());
			}// if

			create_img_table();
		}// if
	}// if
	std::cout << " ID: " << m_db->id() << std::endl;
	std::cout << " Last Error: " << m_db->last_error() << std::endl;
	std::cout << "====================================" << std::endl << std::endl;

	read_root_folder();
}

void test_image_loader::run()
{
	uint32_t images_found = 0;
	uint32_t images_inserted = insert_img_records(images_found);
}

void test_image_loader::cleanup()
{

}

void test_image_loader::create_img_table()
{
	std::unique_ptr< ese::table > table(new ese::table(*m_db));
	if (!table->create("Images"))
	{
		throw std::exception((ese::error(table->last_error()) + ".").c_str());
	}
	table->create_column("path", JET_coltypLongText, 0, 1024);
	table->create_column("name", JET_coltypText, 0);
	table->create_column("extension", JET_coltypText, 0);
	table->create_column("update_date", JET_coltypDateTime);
	table->create_column("insert_date", JET_coltypDateTime);
	table->create_column("file_size", JET_coltypLong);
	table->create_column("version", JET_coltypLong, JET_bitColumnVersion);
	table->create_column("image", JET_coltypLongBinary, JET_bitColumnTagged);
	if (!table->detect_columns() || (table->column_count() != 8))
	{
		throw std::invalid_argument("Columns Create ERROR.");
	}//if
	if (!table->create_index("IX_Images_path", "+path"))
	{
		throw std::invalid_argument("Index ERROR : " + ese::error(table->last_error()) + ". (path)");
	}//if
	if (!table->create_index("IX_Images_name", "+name"))
	{
		throw std::invalid_argument("Index ERROR : " + ese::error(table->last_error()) + ". (name)");
	}//if
	if (!table->create_index("IX_Images_extension", "+extension"))
	{
		throw std::invalid_argument("Index ERROR : " + ese::error(table->last_error()) + ". (extension)");
	}//if
	if (!table->create_index("IX_Images_update_date", "-update_date"))
	{
		throw std::invalid_argument("Index ERROR : " + ese::error(table->last_error()) + ". (update_date)");
	}//if
	if (!table->create_index("IX_Images_insert_date", "-insert_date"))
	{
		throw std::invalid_argument("Index ERROR : " + ese::error(table->last_error()) + ". (insert_date)");
	}//if
	if (!table->create_index("IX_Images_crete_file_size", "+file_size"))
	{
		throw std::invalid_argument("Index ERROR : " + ese::error(table->last_error()) + ". (file_size)");
	}//if
}

void test_image_loader::read_root_folder()
{
	std::cout << "ROOT IMAGE FOLDER (Example c:\\test) >";
	std::string s;
	std::cin >> m_parse_root; std::getline(std::cin, s);
	if (!s.empty())
	{
		m_parse_root += " " + s;
	}

	path p(m_parse_root);
	if (!exists(p))
	{
		throw std::invalid_argument( "Path '" + p.string() + "' not found!" );
	}

}

uint32_t test_image_loader::insert_img_records(uint32_t& found)
{
	uint32_t res = found = 0;
	path root(m_parse_root);
	std::string extension;
	std::unique_ptr< ese::table > img_table(new ese::table(*m_db));

	if (!img_table->open("Images") || !img_table->set_index("IX_Images_path"))
	{
		throw std::invalid_argument("ERROR : " + ese::error(img_table->last_error()) + ".");
	}// if

	ese::table::bookmark bk;
	ese::column& cPath = img_table->column("path");
	ese::column& cName = img_table->column("name");
	ese::column& cExt = img_table->column("extension");
	ese::column& cUpdateDt = img_table->column("update_date");
	ese::column& cInsertDt = img_table->column("insert_date");
	ese::column& cSize = img_table->column("file_size");
	ese::column& cImg = img_table->column("image");

	std::string sP;
	std::vector<uint8_t> img;
	uint32_t fileSize;
	m_ses->begin_transaction();

	for (auto I = recursive_directory_iterator(root); I != recursive_directory_iterator(); ++I)
	{
		const auto& fP = I->path();
		if (!is_directory(fP) && is_regular_file(fP))
		{
			extension = fP.extension();
			std::transform(extension.begin(), extension.end(), extension.begin(), ::toupper);
			if ((extension == ".JPG")
				|| (extension == ".JPEG")
				|| (extension == ".GIF")
				|| (extension == ".BMP")
				|| (extension == ".PNG")
				|| (extension == ".ICO"))
			{
				sP = fP.string();
				if ((fileSize = static_cast<uint32_t>(file_size(fP))) >= static_cast<uint32_t>(std::numeric_limits<int32_t>::max()))
				{
					// the file is too big
					continue;
				}
				try
				{
					std::ifstream is(sP, std::ios::binary || std::ios::in);
					img.resize(fileSize);
					is.seekg(0, std::ios::beg);
					is.read(reinterpret_cast<char *>(&img[0]), fileSize);
					is.close();
				}// try
				catch (...)
				{
					continue;
				}
				if (img_table->prepare_insert())
				{
					++found;
					cPath.set(sP);
					cName.set(fP.filename());
					cExt.set(extension);
					cUpdateDt.set(last_write_time(fP));
					cInsertDt.set(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
					cSize.set(file_size(fP));
					cImg.set(img);
					if (img_table->update(bk))
					{
						++res;
					}// if
				}// if

			}// if
		}// if
	} // for

	m_ses->commit();
	img_table->close();

	return res;
}
